/*******************************************************************************
 * Copyright (c) 2011, 2016 Eurotech and/or its affiliates
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Eurotech
 *******************************************************************************/
package org.eclipse.kura;

import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The KuraException class is the superclass of all errors and exceptions in the Kura project. It extends the JDK Exception class by requesting its invokers to provide an error code when
 * building its instances. The code is one value of KuraErrorCode enum; the code is used to document the possible error conditions generated by the platform as well as to identify the localized
 * exception messages to be reported. Exceptions messages are stored in the KuraExceptionMessagesBundle Properties Bundle and they are keyed on the exception code.
 */
public class KuraException extends Exception 
{
	private static final long serialVersionUID = 7468633737373095296L;

	private static final Logger s_logger = LoggerFactory.getLogger(KuraException.class);
	
	private static final String KURA_GENERIC_MESSAGES_PATTERN = "Generic Error - {0}: {1}";
	private static final String KURA_EXCEPTION_MESSAGES_BUNDLE = "org.eclipse.kura.core.messages.KuraExceptionMessagesBundle";

	//TODO - add back when logger is working
	//private static final Logger s_logger = LoggerFactory.getLogger(KuraException.class);

	protected KuraErrorCode m_code;
	private Object[] m_arguments;

	@SuppressWarnings("unused")
	private KuraException() {
		super();
	}

	/**
	 * Builds a new EdcException instance based on the supplied EdcErrorCode.
	 * 
	 * @param code
	 */
	public KuraException(KuraErrorCode code) {
		m_code = code;
	}

	/**
	 * Builds a new EdcException instance based on the supplied EdcErrorCode.
	 * 
	 * @param code
	 * @param arguments
	 */
	public KuraException(KuraErrorCode code, Object... arguments) {
		m_code = code;
		m_arguments = arguments;
	}

	/**
	 * Builds a new EdcException instance based on the supplied EdcErrorCode, an optional Throwable cause, and optional arguments for the associated exception message.
	 * 
	 * @param code
	 * @param cause
	 * @param arguments
	 */
	public KuraException(KuraErrorCode code, Throwable cause, Object... arguments) {
		super(cause);
		m_code = code;
		m_arguments = arguments;
	}

	/**
	 * Factory method to build an EdcException with the EdcErrorCode.INTERNAL_ERROR code providing a cause and a message.
	 * 
	 * @param cause
	 * @param message
	 * @return
	 */
	public static KuraException internalError(Throwable cause, String message) {
		return new KuraException(KuraErrorCode.INTERNAL_ERROR, cause, message);
	}

	/**
	 * Factory method to build an EdcException with the EdcErrorCode.INTERNAL_ERROR code providing a cause and a message.
	 * 
	 * @param cause
	 * @return
	 */
	public static KuraException internalError(Throwable cause) {
		return new KuraException(KuraErrorCode.INTERNAL_ERROR, cause, "");
	}

	/**
	 * Factory method to build an EdcException with the EdcErrorCode.INTERNAL_ERROR code providing only a message.
	 *
	 * @param message
	 * @return
	 */
	public static KuraException internalError(String message) {
		return new KuraException(KuraErrorCode.INTERNAL_ERROR, null, message);
	}

	
	public KuraErrorCode getCode() {
		return m_code;
	}

	
	public String getMessage() {
		return getLocalizedMessage(Locale.US);
	}

	
	public String getLocalizedMessage() {
		return getLocalizedMessage(Locale.getDefault());
	}

	
	private String getLocalizedMessage(Locale locale) 
	{
		String pattern = getMessagePattern(locale, m_code);
		if (m_code == null || KuraErrorCode.INTERNAL_ERROR.equals(m_code)) {
			if (m_arguments != null && m_arguments.length > 1) {
				// append all arguments into a single one			
				StringBuilder sbAllArgs = new StringBuilder();
				for (Object arg : m_arguments) {
					sbAllArgs.append(" - ");
					sbAllArgs.append(arg);
				}
				m_arguments = new Object[] {sbAllArgs.toString()};
			}
		}
		String message = MessageFormat.format(pattern, m_arguments);
		return message;
	}
	

	private String getMessagePattern(Locale locale, KuraErrorCode code) 
	{
		//
		// Load the message pattern from the bundle
		String messagePattern = null;
		ResourceBundle resourceBundle = null;
		try {
			
			resourceBundle = ResourceBundle.getBundle(KURA_EXCEPTION_MESSAGES_BUNDLE, locale);
			if (resourceBundle != null && code != null) {
				messagePattern = resourceBundle.getString(code.name());
				if (messagePattern == null) {
					s_logger.warn("Could not find Exception Messages for Locale {} and code {}", locale, code);
				}
			}
		} catch (MissingResourceException mre) {
			// log the failure to load a message bundle
			s_logger.warn("Could not load Exception Messages Bundle for Locale {}", locale);
		}

		//
		// If no bundle or code in the bundle is found, use a generic message
		if (messagePattern == null) {
			if (code != null) {
				// build a generic message format
				messagePattern = MessageFormat.format(KURA_GENERIC_MESSAGES_PATTERN, code.name());
			}
			else {
				// build a generic message format
				messagePattern = MessageFormat.format(KURA_GENERIC_MESSAGES_PATTERN, "Unknown");			
			}
		}

		return messagePattern;
	}
}
